package risoe.syslab.fhnew.util;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.ListIterator;
import risoe.syslab.model.CompositeMeasurement;

public class TimeSeries implements Serializable {

  private static final long serialVersionUID = -87983016969479942L;
  public LinkedList<CompositeMeasurement> data;
  

  public long length;      // Length in microseconds


  public TimeSeries() {
    //Default length of data series is one day 
    this(86400); 
  }

  public TimeSeries(int length) {
    this.length = length*1000000L;
    this.data = new LinkedList<CompositeMeasurement>();
  }

  public boolean addMeasurement(CompositeMeasurement m){
    //Don't add incorrect data
    if(Double.isNaN(m.value)) return false;
    data.add(m);
    CompositeMeasurement first = data.getFirst();
    if (first == null) return true;
    //Clean up outside 
    while((m.timestampMicros-first.timestampMicros) > length){
      data.remove();
      first = data.getFirst();
    }
    return true;
    
  }

  public int getSize(){
    return data.size();
  }

  public CompositeMeasurement getFirstMeasurement() {
    CompositeMeasurement result = null;
    try {
      result = data.getFirst();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
  }

  public CompositeMeasurement getLastMeasurement() {
    CompositeMeasurement result = null;
    try {
      result = data.getLast();
    } catch (Exception e) {

    }
    return result;
  }

  public CompositeMeasurement[] getAll() {
    try {
      return data.toArray(new CompositeMeasurement[data.size()]);    
    } catch (Exception e) {
      System.err.println( "Got exception; array has " + data.size() + " elements" );
      e.printStackTrace();
    }
    return null;
  }

  public CompositeMeasurement[] getLastMeasurements(long seconds) {
    try {
      return getMeasurements(data.getLast().timestampMicros - (seconds*1000000L), data.getLast().timestampMicros);     
    } catch (Exception e) {
      e.printStackTrace();
    }
    return new CompositeMeasurement[0];
  }

  private CompositeMeasurement[] getMeasurements(long from, long to) {


    if(from > to || to < data.getFirst().timestampMicros) return new CompositeMeasurement[0];

    from = Math.max(data.getFirst().timestampMicros, from);
    to = Math.max(Math.min(data.getLast().timestampMicros, to), from + 1);

    int fromIndex = 0;
    int toIndex = data.size();

    while(data.get(--toIndex).timestampMicros >= to); 

    fromIndex = toIndex++;
    if(fromIndex == 0) return new CompositeMeasurement[0];

    while(data.get(--fromIndex).timestampMicros > from);

    return data.subList(fromIndex, toIndex+1).toArray(new CompositeMeasurement[0]);
  } 

  public String toString(){
    StringBuffer result = new StringBuffer();

    ListIterator<CompositeMeasurement> iter = data.listIterator();

    while(iter.hasNext()){
      result.append(iter.next().toString() + "\n");
    }   

    return result.toString();
  }

  public double getMaxValue() {
    Double max = null;
    for (CompositeMeasurement m : data) {
      if (max == null || m.value>max) max = m.value;
    }
    return max;
  }

  public double getMinValue() {
    Double min = null;
    for (CompositeMeasurement m : data) {
      if (min == null || m.value<min) min = m.value;
    }
    return min;
  }
}